package storage;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.io.FileOutputStream;
import java.io.File;
import java.io.IOException;

import org.jdom.output.XMLOutputter;
import org.jdom.input.SAXBuilder;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.Text;

import storage.beans.GridBean;
import storage.beans.CellBean;
import storage.beans.SimulationBean;
import storage.beans.Region;
import storage.statistics.StatisticFactory;
import storage.statistics.Statistic;

import org.jdom.JDOMException;

public class XmlDataStorage implements IDataStorage {

  private DecimalFormat decimal;
  
	/*
	 * Writes all the necessary simulation data to an XML file.
	 */
	public void saveSimulation(SimulationBean sim) throws IOException, JDOMException {
		File file = new File(getDataFileName(sim.getTilt(), sim.getEccentricity()));
				
		Document doc;
		Element root;
		if (file.exists()) {
			doc = new SAXBuilder().build(file);
			root = doc.getRootElement();
		}
		else {
			root = new Element("simulations");
			doc = new Document(root);
		}
		
		Element simulation = new Element("simulation");
		simulation.setAttribute("tilt", sim.getTilt());
		simulation.setAttribute("eccentricity", sim.getEccentricity());
		simulation.setAttribute("precision", sim.getPrecision());
		simulation.setAttribute("samplingRate", sim.getSamplingRate());
		simulation.setAttribute("temporalAccuracy", sim.getTemporalAccuracy());
		simulation.setAttribute("timestamp", sim.getTimestamp());
		root.addContent(simulation);
    
    setDataPrecision(Integer.parseInt(sim.getPrecision()));
		
		Element settings = new Element("settings");
		settings.setAttribute("gridSpacing", sim.getGridSpacing());
		settings.setAttribute("timeStep", sim.getTimeStep());
		settings.setAttribute("startDateTime", sim.getStartDateTimeString());
		settings.setAttribute("endDateTime", sim.getEndDateTimeString());
		simulation.addContent(settings);
		
		sim.initStatIterator ();
		for (Statistic stat = sim.getNextStatistic (); stat != null; stat = sim.getNextStatistic()) {
			Element statistic = new Element ("statistic");
			statistic.setAttribute ("type", stat.getType());
			statistic.addContent (stat.getStatValues());
      root.addContent(statistic);
		}
		
		Element snapshots = new Element("snapshots");
    snapshots.setAttribute("radius", sim.getRadius());
		simulation.addContent(snapshots);
		
		sim.initGridIterator();
		for (GridBean gb = sim.getNextGrid(); gb != null; gb = sim.getNextGrid ()) {
			Element grid = new Element("grid");
			grid.setAttribute("dateTime", gb.getDateTime());
      grid.setAttribute("width", gb.getWidth());
      grid.setAttribute("height", gb.getHeight());
      
			CellBean[][] cellBeans = gb.getCellBeans();
      for(int y = 0; y < cellBeans.length; y++) {
        for(int x = 0; x < cellBeans[y].length; x++) {
          CellBean cb = cellBeans[y][x];
  				if(cb != null) {
            Element cell = new Element("cell");
    				cell.setAttribute("latNorth", cb.getLatNorth());
            cell.setAttribute("latSouth", cb.getLatSouth());
            cell.setAttribute("longEast", cb.getLongEast());
            cell.setAttribute("longWest", cb.getLongWest());
    				cell.setAttribute("temperature", decimal.format(cb.getTemperature()));
            cell.setAttribute("xy", cb.x()+","+cb.y());
            cell.setAttribute("height", String.valueOf(cb.getCellHeight()));
    				grid.addContent(cell);
          }
        }
      }			
			snapshots.addContent(grid);
		}
    
		Region regionBean = sim.getRegion ();
		Element region = new Element ("region");
		region.setAttribute ("longWest", String.valueOf (regionBean.getLongWest()));
		region.setAttribute ("longEast", String.valueOf (regionBean.getLongEast()));
		region.setAttribute ("latNorth", String.valueOf (regionBean.getLatNorth()));
		region.setAttribute ("latSouth", String.valueOf (regionBean.getLatSouth()));
		simulation.addContent (region);

		XMLOutputter writer = new XMLOutputter();
		writer.output(doc, new FileOutputStream(file));
	}
	
	private String getDataFileName(String tilt, String eccentricity) {
		File placeholder = new File(getClass().getResource("placeholder").getPath());
		String dataDir = placeholder.getParentFile().getPath().replaceAll("%20", " ")+ System.getProperty("file.separator");
		return dataDir + tilt + "-" + eccentricity + "-data.xml";
	}
  
  private void setDataPrecision(int precision) {
    if(precision == -1) {
      decimal = new DecimalFormat();
    }
    else {
      String formatPatternString = "0.";
      for(int i = 0; i < precision; i++) {
        formatPatternString += "0";
      }
      decimal = new DecimalFormat(formatPatternString);
    }
  }

	public List getSimulations(int tilt, float eccentricity)
			throws IOException, JDOMException, NoSuchElementException {
		File file = new File(getDataFileName(String.valueOf(tilt), String.valueOf(eccentricity)));		
		ArrayList simulations = new ArrayList();
		
		if (!file.exists()) {
			//do nothing, leave the list blank
		}
		else {
      StatisticFactory statFactory = StatisticFactory.getInstance();
			Document doc = new SAXBuilder().build(file);
			Element root = doc.getRootElement();
      
			for (Iterator simIterator = root.getChildren("simulation").iterator(); simIterator.hasNext(); ) {
				SimulationBean simBean = new SimulationBean();
	
				Element elSimulation = (Element) simIterator.next();
				simBean.setTilt(Integer.parseInt(elSimulation.getAttributeValue("tilt")));
				simBean.setEccentricity(Float.parseFloat(elSimulation.getAttributeValue("eccentricity")));
				simBean.setPrecision(Integer.parseInt(elSimulation.getAttributeValue("precision")));
				simBean.setSamplingRate(Integer.parseInt(elSimulation.getAttributeValue("samplingRate")));
				simBean.setTemporalAccuracy(Integer.parseInt(elSimulation.getAttributeValue("temporalAccuracy")));
	
				Element settings = elSimulation.getChild("settings");
				simBean.setGridSpacing(Integer.parseInt(settings.getAttributeValue("gridSpacing")));
				simBean.setTimeStep(Integer.parseInt(settings.getAttributeValue("timeStep")));
				simBean.setStartDateTime(Long.parseLong(settings.getAttributeValue("startDateTime")));
				simBean.setEndDateTime(Long.parseLong(settings.getAttributeValue("endDateTime")));
	
				for (Iterator statsIterator = elSimulation.getChildren("statistic").iterator(); statsIterator.hasNext(); ) {
					Element statElement = (Element) statsIterator.next();
          Statistic statistic = statFactory.getStatistic(statElement.getAttributeValue("type"), 
                                                         getElementTextContents(statElement));
					simBean.addStatistic(statistic);
				}
        
				Element snapshots = elSimulation.getChild("snapshots");
        simBean.setRadius(Integer.parseInt(snapshots.getAttributeValue("radius")));
				for (Iterator snapshotIterator = snapshots.getChildren("grid").iterator(); snapshotIterator.hasNext();) {
					Element gridElement = (Element) snapshotIterator.next();
					ArrayList cellBeanList = new ArrayList();
          for (Iterator cellIterator = gridElement.getChildren("cell").iterator(); cellIterator.hasNext();) {
						Element elCell = (Element) cellIterator.next();
            int latNorth = Integer.parseInt(elCell.getAttributeValue("latNorth"));
            int latSouth = Integer.parseInt(elCell.getAttributeValue("latSouth"));
            int longEast = Integer.parseInt(elCell.getAttributeValue("longEast"));
            int longWest = Integer.parseInt(elCell.getAttributeValue("longWest"));
            float temperature = Float.parseFloat(elCell.getAttributeValue("temperature"));
            float height = Float.parseFloat(elCell.getAttributeValue("height"));
						String[] xy = elCell.getAttributeValue("xy").split(",");
            int x = Integer.parseInt(xy[0]);
            int y = Integer.parseInt(xy[1]);
            CellBean cellBean = new CellBean(new Region(longWest, longEast, latNorth, latSouth), temperature, x, y, height);
						cellBeanList.add(cellBean);
					}
          long dateTime = Long.parseLong(gridElement.getAttributeValue("dateTime"));
          int width = Integer.parseInt(gridElement.getAttributeValue("width"));
          int height = Integer.parseInt(gridElement.getAttributeValue("height"));
          simBean.addSnapshot(new GridBean(dateTime, width, height, cellBeanList));
				}
        
				Element region = (Element) elSimulation.getChild ("region");
				Region regionBean = new Region( 
                                  Integer.parseInt(region.getAttributeValue("longWest")), 
                                  Integer.parseInt(region.getAttributeValue("longEast")), 
                                  Integer.parseInt(region.getAttributeValue("latNorth")), 
                                  Integer.parseInt(region.getAttributeValue("latSouth")));
				simBean.setRegion(regionBean);
				simulations.add(simBean);
			}
		}
		return simulations;
	}
  
  private String getElementTextContents(Element el) {
    String contents = "";
    List contentList = el.getContent();
    for(Iterator itr = contentList.iterator(); itr.hasNext();) {
      Object contentType = itr.next();
      if(contentType instanceof Text) {
        contents = ((Text) contentType).getText();
        break;
      }
    }
    return contents;
  }
}
